package com.hqd.ch03.v11;

import com.hqd.ch03.v11.beans.BeanWrapper;
import com.hqd.ch03.v11.beans.BeanWrapperImpl;
import com.hqd.ch03.v11.config.BeanDefinition;
import com.hqd.ch03.v11.config.MutablePropertyValues;
import com.hqd.ch03.v11.config.Scope;
import com.hqd.ch03.v11.factory.BeanFactory;
import com.hqd.ch03.v11.factory.ConfigurableBeanFactory;
import com.hqd.ch03.v11.factory.FactoryBean;
import com.hqd.ch03.v11.io.ResourceLoader;
import com.hqd.ch03.v11.io.support.DefaultResourceLoader;
import com.hqd.ch03.v11.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v11.registry.support.DefaultSingletonBeanRegistry;
import com.hqd.ch03.v11.registry.support.SimpleBeanDefinitionRegistry;
import com.hqd.ch03.v11.spel.BeanExpressionResolver;
import com.hqd.ch03.v11.spel.express.BeanExpressionContext;
import com.hqd.ch03.v11.spel.express.StandardBeanExpressionResolver;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyEditor;
import java.util.HashMap;
import java.util.Map;

public abstract class SpringImitationV11 extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
    private final Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();
    protected BeanDefinitionRegistry beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
    protected BeanExpressionResolver beanExpressionResolver = new StandardBeanExpressionResolver();
    /**
     * 单例缓存
     */
    protected Map<String, Scope> scopeCache = new HashMap<>();
    /**
     * 资源加载器
     */
    protected ResourceLoader resourceLoader = new DefaultResourceLoader();

    public static boolean isFactoryDereference(String name) {
        return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
    }

    @Override
    public void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass) {
        customEditors.put(requiredType, propertyEditorClass);
    }

    /**
     * 处理传入的name，去除&开头
     *
     * @param name
     * @return
     */
    protected String transformedBeanName(String name) {
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        String beanName = name;
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    }

    /**
     * 区分正常bean还是FactoryBean
     *
     * @param obj
     * @param name
     * @param beanName
     * @param bd
     * @return
     */
    protected Object getObjectForBeanInstance(Object obj, String name, String beanName, BeanDefinition bd) {
        if (isFactoryDereference(name)) {
            if (!(obj instanceof FactoryBean)) {
                throw new RuntimeException(String.format("%s 未实现FactoryBean接口", beanName));
            }
            return obj;
        }
        if (obj instanceof FactoryBean) {
            return ((FactoryBean) obj).getObject();
        }
        return obj;
    }

    private void initBean(BeanWrapper beanWrapper, BeanDefinition bd) {
        try {
            MutablePropertyValues properties = bd.getProperties();
            properties.getProperties().forEach(propertyValue -> {
                boolean isRef = propertyValue.isRef();
                Object value = propertyValue.getValue();
                if (isRef) {
                    value = getBean((String) value);
                }
                try {
                    if (value instanceof String) {
                        /**
                         * 使用Spel进行解析
                         */
                        value = beanExpressionResolver.evaluate((String) value, new BeanExpressionContext(this));
                    }
                    beanWrapper.setPropertyValue(propertyValue.getName(), value, isRef);
                    //BeanUtils.setProperty(bean, propertyValue.getName(), value);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private Object createBean(BeanDefinition bd, String beanName) {
        try {
            Object instance = null;
            if (bd.isSingleton()) {//单例
                instance = getSingleton(beanName);
                if (instance == null) {
                    instance = doCreate(bd, beanName);
                }
                addSingleton(beanName, instance);
            } else if (bd.isPrototype()) {//多例
                instance = doCreate(bd, beanName);
            } else {//自定义作用域
                String scopeName = bd.getScope();
                Scope scope = scopeCache.get(scopeName);
                if (scope == null) {
                    throw new RuntimeException(String.format("[%s]未注册scope", scopeName));
                }
                instance = scope.get(beanName, () -> doCreate(bd, beanName));
            }
            return instance;

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected Object doCreate(BeanDefinition bd, String beanName) {
        String beanClass = bd.getBeanClass();
        try {
            Object instance = Class.forName(beanClass).getConstructor().newInstance();
            BeanWrapper beanWrapper = new BeanWrapperImpl(instance);
            //初始化BeanWrapper
            initBeanWrapper(beanWrapper);
            addSingletonFactory(beanName, () -> beanWrapper.getWrappedInstance());
            //doSomething
            initBean(beanWrapper, bd);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected void initBeanWrapper(BeanWrapper bw) {
        /**
         * 注册自定义类型转换器
         *
         */
        registerCustomEditors(bw);
    }

    @Override
    public boolean containsBean(String name) {
        String beanName = transformedBeanName(name);
        if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
            return true;
        }
        return false;
    }

    public boolean containsBeanDefinition(String beanName) {
        return this.beanDefinitionRegistry.containsBeanDefinition(beanName);
    }

    /**
     * 是否应该区分全局转换器和局部转换器
     * 全局为Factory级别，局部为Bean级别
     *
     * @param bw
     */
    protected void registerCustomEditors(BeanWrapper bw) {
        if (MapUtils.isNotEmpty(customEditors)) {
            customEditors.forEach((key, val) -> {
                try {
                    bw.registerCustomEditor(key, val.getConstructor().newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    @Override
    public void registerScope(String scopeName, Scope scope) {
        if (StringUtils.isNotBlank(scopeName) && scope != null) {
            scopeCache.put(scopeName, scope);
        }
    }

    @Override
    public <T> T getBean(String name, Class<T> clazz) {
        return (T) doGetBean(name);
    }

    @Override
    public Object getBean(String name) {
        return doGetBean(name);
    }

    protected Object doGetBean(String name) {
        String beanName = transformedBeanName(name);
        BeanDefinition bd = beanDefinitionRegistry.getBeanDefinition(beanName);
        Object bean = createBean(bd, beanName);
        bean = getObjectForBeanInstance(bean, name, beanName, bd);
        return bean;
    }


    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}